package qfcfg

import (
	"encoding/xml"
	"io/ioutil"
	"os"
	"path"

	"github.com/fsnotify/fsnotify"
	"github.com/qf-tech/qftech-base/pkg/log"
	"github.com/qf-tech/qftech-base/pkg/qfcrypt"
)

// SConfig def
type SConfig struct {
	XMLName xml.Name `xml:"config"` // 指定最外层的标签为config
	Server  SServer  `xml:"server"` // 读取smtpServer配置项，并将结果保存到SmtpServer变量中
	Mysql   SMysql   `xml:"mysql"`
	Pgsql   SPgsql   `xml:"pgsql"`
}

// SServer def
type SServer struct {
	IP        string   `xml:"ip"`         // 读取ip
	AgentHost string   `xml:"agent_host"` // 读取 agent_host
	Enable    int      `xml:"enable"`     // 读取enable
	Port      int      `xml:"port"`       // 读取port
	Notice    string   `xml:"notice"`     // 读取notice
	Auth      SAuth    `xml:"auth"`       // 读取auth
	Convert   SConvert `xml:"convert"`    // 文件格式转换是否使能
	Report    SReport  `xml:"report"`     // 报告相关配置
	IPInfo    SIPInfo  `xml:"ip_info"`    // IP信息获取配置
	FileSvr   SFileSvr `xml:"filesvr"`    // 静态文件服务
}

// SAuth def
type SAuth struct {
	Enable int    `xml:"enable"` // 读取enable
	Redis  SRedis `xml:"redis"`  // 读取redis
}

// PassWord def
type PassWord struct {
	Encrypt bool   `xml:"encrypt"` // 读取encrypt
	Value   string `xml:"value"`   // 读取value
}

// SRedis def
type SRedis struct {
	Host     string   `xml:"host"`     // 读取host
	Port     int      `xml:"port"`     // 读取port
	Password PassWord `xml:"password"` // 读取password
	Timeout  int      `xml:"timeout"`  // 读取timeout
}

type SConvert struct {
	Enable bool `xml:"enable"`
}

type SReport struct {
	TokenConfig STokenConfig `xml:"token"`
}

type SIPInfo struct {
	Mode          string `xml:"mode"`
	QueryLocation bool   `xml:"query_location"`
}

type FileSvrMode string

const (
	FileSvrPureMode FileSvrMode = "pure"
)

type SFileSvr struct {
	URL  string      `xml:"url"`
	Dir  string      `xml:"dir"`
	Mode FileSvrMode `xml:"mode"`
}

type STokenConfig struct {
	Key    string `xml:"key"`
	Expire string `xml:"expire"`
}

// SMysql def
type SMysql struct {
	Host     string   `xml:"host"`     // 读取host
	Port     int      `xml:"port"`     // 读取port
	Username string   `xml:"username"` // 读取username
	Password PassWord `xml:"password"` // 读取password
	Dbname   string   `xml:"dbname"`   // 读取dbname
	Debug    bool     `xml:"debug"`    // 读取debug
}

type SPgsql struct {
	Host      string   `xml:"host"`       // 读取host
	Port      int      `xml:"port"`       // 读取port
	Username  string   `xml:"username"`   // 读取username
	Password  PassWord `xml:"password"`   // 读取password
	Dbname    string   `xml:"dbname"`     // 读取dbname
	EnableSSL bool     `xml:"enable_ssl"` // 读取enable_ssl
}

// ServerConfig export var
var ServerConfig SConfig

// decryptPassWord def
func decryptPassWord(pwd *PassWord, note string) error {
	if pwd.Encrypt {
		decData, err := qfcrypt.ConfigAes.Decrypt(pwd.Value, "QFTECH")
		if err != nil {
			log.Sugare.Errorf("%s password decrypt err: %v", note, err)
			return err
		}

		decDataStr := string(decData[:])
		pwd.Value = decDataStr
		log.Sugare.Infof("%s password decrypt succeed, plain data: %s", note, decDataStr)
	}

	return nil
}

// readConfig def
func readConfig(path string) {
	file, err := os.Open(path) // For read access.
	if err != nil {
		log.Sugare.Errorf("error: %v", err)
		return
	}
	defer file.Close()
	data, err := ioutil.ReadAll(file)
	if err != nil {
		log.Sugare.Errorf("error: %v", err)
		return
	}

	ServerConfig = SConfig{}
	err = xml.Unmarshal(data, &ServerConfig)
	if err != nil {
		log.Sugare.Errorf("error: %v", err)
		return
	}

	// 敏感信息解密
	_ = decryptPassWord(&ServerConfig.Server.Auth.Redis.Password, "redis")
	_ = decryptPassWord(&ServerConfig.Mysql.Password, "mysql")
}

var watcher *fsnotify.Watcher

// InitConfig def
func InitConfig() {
	filePath := "./conf/config.xml"
	readConfig(filePath)

	var err error
	// 监控文件
	watcher, err = fsnotify.NewWatcher()
	if err != nil {
		log.Sugare.Errorf("error:", err)
	}
	// defer watcher.Close()

	// done := make(chan bool)
	go func() {
		for {
			select {
			case event, ok := <-watcher.Events:
				if !ok {
					return
				}
				log.Sugare.Infof("event: %v", event)
				if event.Op&fsnotify.Write == fsnotify.Write && path.Base(event.Name) == filePath {
					log.Sugare.Infof("modified file: %s", event.Name)
					readConfig(filePath)
				}
			case err, ok := <-watcher.Errors:
				if !ok {
					return
				}
				log.Sugare.Errorf("error: %v", err)
			}
		}
	}()

	err = watcher.Add("./conf")
	if err != nil {
		log.Sugare.Errorf("error: %v", err)
	}
	// <-done
}
